"""
缓存管理系统 V2.0
完全符合实践要求：LRU替换策略、命中统计、替换日志
"""

import time
from typing import Dict, Optional, List, Tuple
from collections import OrderedDict

class CacheFrame:
    """缓存帧"""
    
    def __init__(self, page_id: int, data: bytearray):
        """初始化缓存帧"""
        self.page_id = page_id
        self.data = data
        self.dirty = False
        self.pinned = False
        self.last_access = time.time()
        self.access_count = 1
    
    def access(self):
        """访问页面"""
        self.last_access = time.time()
        self.access_count += 1
    
    def mark_dirty(self):
        """标记为脏页"""
        self.dirty = True
    
    def pin(self):
        """固定页面"""
        self.pinned = True
    
    def unpin(self):
        """取消固定页面"""
        self.pinned = False

class CacheStatistics:
    """缓存统计信息"""
    
    def __init__(self):
        """初始化统计信息"""
        self.hit_count = 0
        self.miss_count = 0
        self.read_count = 0
        self.write_count = 0
        self.evict_count = 0
        self.flush_count = 0
    
    def hit(self):
        """缓存命中"""
        self.hit_count += 1
        self.read_count += 1
    
    def miss(self):
        """缓存未命中"""
        self.miss_count += 1
        self.read_count += 1
    
    def write(self):
        """写入操作"""
        self.write_count += 1
    
    def evict(self):
        """页面驱逐"""
        self.evict_count += 1
    
    def flush(self):
        """页面刷新"""
        self.flush_count += 1
    
    def get_hit_rate(self) -> float:
        """获取命中率"""
        total = self.hit_count + self.miss_count
        return self.hit_count / total if total > 0 else 0.0
    
    def reset(self):
        """重置统计信息"""
        self.hit_count = 0
        self.miss_count = 0
        self.read_count = 0
        self.write_count = 0
        self.evict_count = 0
        self.flush_count = 0

class CacheLog:
    """缓存日志"""
    
    def __init__(self):
        """初始化日志"""
        self.logs = []
    
    def log_hit(self, page_id: int):
        """记录命中日志"""
        self.logs.append({
            'timestamp': time.time(),
            'type': 'HIT',
            'page_id': page_id,
            'message': f"页面 {page_id} 缓存命中"
        })
    
    def log_miss(self, page_id: int):
        """记录未命中日志"""
        self.logs.append({
            'timestamp': time.time(),
            'type': 'MISS',
            'page_id': page_id,
            'message': f"页面 {page_id} 缓存未命中"
        })
    
    def log_evict(self, page_id: int, reason: str = "LRU"):
        """记录驱逐日志"""
        self.logs.append({
            'timestamp': time.time(),
            'type': 'EVICT',
            'page_id': page_id,
            'message': f"页面 {page_id} 被驱逐，原因: {reason}"
        })
    
    def log_flush(self, page_id: int):
        """记录刷新日志"""
        self.logs.append({
            'timestamp': time.time(),
            'type': 'FLUSH',
            'page_id': page_id,
            'message': f"页面 {page_id} 刷新到磁盘"
        })
    
    def get_recent_logs(self, count: int = 10) -> List[Dict]:
        """获取最近的日志"""
        return self.logs[-count:] if self.logs else []
    
    def print_logs(self, count: int = 10):
        """打印最近的日志"""
        logs = self.get_recent_logs(count)
        if logs:
            print("缓存操作日志:")
            for log in logs:
                timestamp = time.strftime("%H:%M:%S", time.localtime(log['timestamp']))
                print(f"  [{timestamp}] {log['message']}")
        else:
            print("暂无缓存操作日志")

class CacheManager:
    """缓存管理器"""
    
    def __init__(self, buffer_size: int = 100, storage_system=None):
        """初始化缓存管理器"""
        self.buffer_size = buffer_size
        self.storage_system = storage_system
        self.frames: Dict[int, CacheFrame] = {}
        self.access_order = OrderedDict()  # 用于LRU算法
        self.statistics = CacheStatistics()
        self.log = CacheLog()
    
    def set_storage_system(self, storage_system):
        """设置存储系统"""
        self.storage_system = storage_system
    
    def get_page(self, page_id: int) -> Optional[bytearray]:
        """获取页面"""
        # 检查缓存
        if page_id in self.frames:
            frame = self.frames[page_id]
            frame.access()
            self._update_access_order(page_id)
            self.statistics.hit()
            self.log.log_hit(page_id)
            return frame.data
        
        # 缓存未命中，从存储系统读取
        self.statistics.miss()
        self.log.log_miss(page_id)
        
        if self.storage_system:
            data = self.storage_system.read_page(page_id)
            if data:
                return self._load_page(page_id, data)
        
        return None
    
    def put_page(self, page_id: int, data: bytearray, dirty: bool = False):
        """将页面放入缓存"""
        # 如果页面已存在，更新数据
        if page_id in self.frames:
            frame = self.frames[page_id]
            frame.data = data
            frame.access()
            if dirty:
                frame.mark_dirty()
            self._update_access_order(page_id)
            self.statistics.write()
            return
        
        # 检查缓存是否已满
        if len(self.frames) >= self.buffer_size:
            self._evict_page()
        
        # 创建新的缓存帧
        frame = CacheFrame(page_id, data)
        if dirty:
            frame.mark_dirty()
        
        self.frames[page_id] = frame
        self._update_access_order(page_id)
        self.statistics.write()
    
    def pin_page(self, page_id: int):
        """固定页面，防止被驱逐"""
        if page_id in self.frames:
            self.frames[page_id].pin()
    
    def unpin_page(self, page_id: int):
        """取消固定页面"""
        if page_id in self.frames:
            self.frames[page_id].unpin()
    
    def mark_dirty(self, page_id: int):
        """标记页面为脏页"""
        if page_id in self.frames:
            self.frames[page_id].mark_dirty()
    
    def flush_page(self, page_id: int) -> bool:
        """将页面刷新到磁盘"""
        if page_id not in self.frames:
            return False
        
        frame = self.frames[page_id]
        if frame.dirty and self.storage_system:
            success = self.storage_system.write_page(page_id, frame.data)
            if success:
                frame.dirty = False
                self.statistics.flush()
                self.log.log_flush(page_id)
            return success
        
        return True
    
    def flush_all(self):
        """将所有脏页刷新到磁盘"""
        for page_id in list(self.frames.keys()):
            self.flush_page(page_id)
    
    def _load_page(self, page_id: int, data: bytearray) -> bytearray:
        """加载页面到缓存"""
        self.put_page(page_id, data)
        return data
    
    def _update_access_order(self, page_id: int):
        """更新访问顺序"""
        if page_id in self.access_order:
            self.access_order.move_to_end(page_id)
        else:
            self.access_order[page_id] = True
    
    def _evict_page(self):
        """驱逐页面（LRU算法）"""
        # 找到最久未访问且未被固定的页面
        for page_id in self.access_order:
            frame = self.frames[page_id]
            if not frame.pinned:
                # 如果是脏页，先刷新到磁盘
                if frame.dirty:
                    self.flush_page(page_id)
                
                # 从缓存中移除
                del self.frames[page_id]
                del self.access_order[page_id]
                self.statistics.evict()
                self.log.log_evict(page_id, "LRU")
                return
        
        # 如果没有可驱逐的页面，抛出异常
        raise Exception("缓存已满且所有页面都被固定")
    
    def get_cache_statistics(self) -> Dict:
        """获取缓存统计信息"""
        return {
            'buffer_size': self.buffer_size,
            'used_frames': len(self.frames),
            'hit_count': self.statistics.hit_count,
            'miss_count': self.statistics.miss_count,
            'hit_rate': self.statistics.get_hit_rate(),
            'read_count': self.statistics.read_count,
            'write_count': self.statistics.write_count,
            'evict_count': self.statistics.evict_count,
            'flush_count': self.statistics.flush_count,
            'dirty_frames': sum(1 for frame in self.frames.values() if frame.dirty),
            'pinned_frames': sum(1 for frame in self.frames.values() if frame.pinned)
        }
    
    def print_cache_info(self):
        """打印缓存信息"""
        stats = self.get_cache_statistics()
        print("缓存管理器信息:")
        print(f"  缓冲区大小: {stats['buffer_size']}")
        print(f"  已使用帧数: {stats['used_frames']}/{stats['buffer_size']}")
        print(f"  命中次数: {stats['hit_count']}")
        print(f"  未命中次数: {stats['miss_count']}")
        print(f"  命中率: {stats['hit_rate']:.2%}")
        print(f"  读取次数: {stats['read_count']}")
        print(f"  写入次数: {stats['write_count']}")
        print(f"  驱逐次数: {stats['evict_count']}")
        print(f"  刷新次数: {stats['flush_count']}")
        print(f"  脏页数: {stats['dirty_frames']}")
        print(f"  固定页数: {stats['pinned_frames']}")
    
    def print_cache_logs(self, count: int = 10):
        """打印缓存日志"""
        self.log.print_logs(count)
    
    def clear(self):
        """清空缓存"""
        self.flush_all()
        self.frames.clear()
        self.access_order.clear()
        self.statistics.reset()
        self.log = CacheLog()

# 测试函数
if __name__ == "__main__":
    from page_storage_v2 import PageStorageSystem
    
    # 创建存储系统和缓存管理器
    storage = PageStorageSystem("test_cache")
    cache = CacheManager(5)  # 小缓存用于测试
    cache.set_storage_system(storage)
    
    print("测试缓存管理系统...")
    
    # 测试页面分配和缓存
    print("\n1. 测试页面分配和缓存...")
    page_id1 = storage.allocate_page()
    page_id2 = storage.allocate_page()
    
    # 创建测试数据
    test_data1 = bytearray(4096)
    test_data1[0:4] = b'PAGE'
    test_data2 = bytearray(4096)
    test_data2[0:4] = b'DATA'
    
    # 写入缓存
    cache.put_page(page_id1, test_data1, dirty=True)
    cache.put_page(page_id2, test_data2, dirty=True)
    
    print(f"页面 {page_id1} 和 {page_id2} 已放入缓存")
    
    # 测试缓存命中
    print("\n2. 测试缓存命中...")
    data1 = cache.get_page(page_id1)
    data2 = cache.get_page(page_id2)
    print(f"从缓存读取: {data1[0:4]}, {data2[0:4]}")
    
    # 测试缓存统计
    print("\n3. 缓存统计信息:")
    cache.print_cache_info()
    
    # 测试缓存日志
    print("\n4. 缓存操作日志:")
    cache.print_cache_logs()
    
    # 清理
    storage.deallocate_page(page_id1)
    storage.deallocate_page(page_id2)
    cache.clear()
